import child_process from "child_process";
import path from "path";
import { CliMsg, CliMsgId, miscCfg } from "./util/defines";
import { program } from "commander";
import { TcpClient } from "./tcp/TcpClient";
import { TcpSocket } from "./tcp/TcpSocket";

function optsLowerCase(opts: any) {
    if (opts) {
        for (let key in opts) {
            let val = opts[key];
            delete opts[key];
            opts[key.toLowerCase()] = val;
        }
    }
    return opts;
}

function connectMaster(host: string, port: string | number, token: string, cb: (socket: CliSocket) => void) {
    let socket = new TcpClient(Number(port), host, () => {
        new CliSocket(socket, cb);
    });
};

class CliSocket {

    socket: TcpSocket;
    constructor(socket: TcpSocket, cb: (socket: CliSocket) => void) {
        this.socket = socket;
        socket.sendReg({ token: miscCfg.cliToken });
        socket.on((data: { msgId: CliMsgId, msg: CliMsg }) => {
            this.onHeartbeat(data);
            if (data.msgId === CliMsgId.regOk) {
                cb(this);
                this.sendHeartbeat();
            }
        });
    }

    private _hbTimer: NodeJS.Timeout;
    private _hbTimeoutTimer: NodeJS.Timeout;

    on(onData: (data: { msgId: CliMsgId, msg: CliMsg }) => void) {
        this.socket.on(data => {
            this.onHeartbeat(data)
            onData(data);
        });
    }

    onHeartbeat(data: { msgId: CliMsgId, msg: CliMsg }) {
        if (data.msgId === CliMsgId.heartbeat) {
            clearTimeout(this._hbTimeoutTimer);
            this._hbTimer = setTimeout(this.sendHeartbeat.bind(this), miscCfg.heartbeatTime);
        }
    }

    onRes(onRes: (res: CliMsg<CliMsgId.result>) => void) {
        this.on(data => {
            if (data.msgId === CliMsgId.result) {
                onRes(data.msg as any);
            }
        })
    }

    send<MsgId extends CliMsgId>(msgId: MsgId, msg?: CliMsg<MsgId>) {
        this.socket.send({ msgId, msg });
    }

    close() {
        clearTimeout(this._hbTimer);
        clearTimeout(this._hbTimeoutTimer);
        this.socket.close();
        process.exit();
    }

    private sendHeartbeat() {
        this.send(CliMsgId.heartbeat);
        this._hbTimeoutTimer = setTimeout(this.onHeartbeatTimeout.bind(this), miscCfg.heartbeatTimeout);
    }

    private onHeartbeatTimeout() {
        console.log("cli心跳包超时");
        this.socket.close();
    }
}

program.command("start [mainPath]")
    .description("启动服务器")
    .option("-env <envrionment>", "运行环境", "development")
    .action((mainPath, opts) => {
        opts = optsLowerCase(opts);
        mainPath ||= "./app.js";
        let cp = child_process.spawn(process.execPath, [path.resolve(process.cwd(), mainPath), `env=${opts.env}`]);
        cp.stdout.on('data', data => console.log(data.toString()));
        cp.stderr.on('data', data => console.log(data.toString()));
    })

program.command("stop")
    .description("关闭服务器")
    .option("-host <host>", "服务器ip地址", "localhost")
    .option("-port <port>", "服务器端口", "6606")
    .option("-token <token>", "cli秘钥", miscCfg.cliToken)
    .action(opts => {
        opts = optsLowerCase(opts);
        connectMaster(opts.host, opts.port, opts.token, socket => {
            socket.send(CliMsgId.stop);
            socket.onRes(res => {
                console.log(JSON.stringify(res));
                socket.close();
            });
        });
    });

program.command("cmd <target> <cmd>")
    .description("发送命令给服务器")
    .option("-stype", "发送的是服务器类型")
    .option("-host <host>", "服务器ip地址", "localhost")
    .option("-port <port>", "服务器端口", "6606")
    .option("-token <token>", "cli秘钥", miscCfg.cliToken)
    .action((target, cmd, opts) => {
        opts = optsLowerCase(opts);
        connectMaster(opts.host, opts.port, opts.token, socket => {
            if (target == "*") {
                socket.send(CliMsgId.cmd, { sess: 0, cmd, all: true });
            } else if (opts.stype) {
                socket.send(CliMsgId.cmd, { sess: 0, cmd, stypes: target.split(",") });
            } else {
                socket.send(CliMsgId.cmd, { sess: 0, cmd, sids: target.split(",") });
            }
            socket.onRes(res => {
                console.log(JSON.stringify(res));
                socket.close();
            });
        });
    });

program.command("reload <msgCfg|msg|rpc>")
    .description("重载消息路由配置或消息或RpcHandler")
    .option("-host <host>", "服务器ip地址", "localhost")
    .option("-port <port>", "服务器端口", "6606")
    .option("-token <token>", "cli秘钥", miscCfg.cliToken)
    .action((reload, opts) => {
        opts = optsLowerCase(opts);
        connectMaster(opts.host, opts.port, opts.token, socket => {
            socket.send(CliMsgId.reload, { sess: 0, reload });
            socket.onRes(res => {
                console.log(JSON.stringify(res));
                socket.close();
            });
        });
    });

program.parse(process.argv);